{
 "metadata": {
  "name": ""
 },
 "nbformat": 3,
 "nbformat_minor": 0,
 "worksheets": [
  {
   "cells": [
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "# Differential Drive Robotics Platform\n",
      "\n",
      "I've been using [SymPy](http://sympy.org) in my research and coursework for a while now. For those that don't know, SymPy is a computer algebra system, capable of performing symbolic calculations that would be too complicated to do by hand. Which makes it perfect for solving the equations needed to generate the equations of motion (EOM) of multibody systems! In this post, I'll demonstrate a simple workflow for generating the EOM for a differential drive robot.\n",
      "\n",
      "## Problem Setup\n",
      "\n",
      "A common robot platform is that of the [differential drive](http://en.wikipedia.org/wiki/Differential_wheeled_robot). The robot is propelled using two wheels, each with their own motor. Usually a caster is used as a third wheel to provide stability. By varying the speeds of the two motors, the robot can change direction.\n",
      "\n",
      "Differential drive platforms are highly maneuverable, as they have a very small turn radius. They are also extremely simple to design. In this example, the dynamics of a differential drive platform will be explored. The resulting model is highly accurate, but makes a few assumptions:\n",
      "\n",
      "**Assumptions**\n",
      "\n",
      "- Wheels roll without slipping\n",
      "- Body is symmetric\n",
      "\n",
      "**The setup is as follows:**"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "from IPython.display import SVG\n",
      "SVG(filename='Robot.svg')"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "metadata": {},
       "output_type": "pyout",
       "prompt_number": 1,
       "svg": [
        "<svg height=\"334.84042\" id=\"svg2985\" inkscape:version=\"0.48.3.1 r9886\" sodipodi:docname=\"Robot.svg\" version=\"1.1\" width=\"830.74847\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\" xmlns:svg=\"http://www.w3.org/2000/svg\">\n",
        "  <sodipodi:namedview bordercolor=\"#666666\" borderopacity=\"1.0\" fit-margin-bottom=\"4\" fit-margin-left=\"4\" fit-margin-right=\"4\" fit-margin-top=\"4\" id=\"namedview2989\" inkscape:current-layer=\"layer1\" inkscape:cx=\"238.25443\" inkscape:cy=\"391.65337\" inkscape:document-units=\"mm\" inkscape:pageopacity=\"0.0\" inkscape:pageshadow=\"2\" inkscape:window-height=\"699\" inkscape:window-maximized=\"1\" inkscape:window-width=\"1366\" inkscape:window-x=\"0\" inkscape:window-y=\"22\" inkscape:zoom=\"0.8683167\" pagecolor=\"#ffffff\" showgrid=\"false\" units=\"mm\"/>\n",
        "  <defs id=\"defs2987\">\n",
        "    <marker id=\"Arrow1Mend\" inkscape:stockid=\"Arrow1Mend\" orient=\"auto\" refX=\"0\" refY=\"0\" style=\"overflow:visible\">\n",
        "      <path d=\"M 0,0 5,-5 -12.5,0 5,5 0,0 z\" id=\"path6861\" inkscape:connector-curvature=\"0\" style=\"fill-rule:evenodd;stroke:#000000;stroke-width:1pt\" transform=\"matrix(-0.4,0,0,-0.4,-4,0)\"/>\n",
        "    </marker>\n",
        "    <marker id=\"Arrow1Lend\" inkscape:stockid=\"Arrow1Lend\" orient=\"auto\" refX=\"0\" refY=\"0\" style=\"overflow:visible\">\n",
        "      <path d=\"M 0,0 5,-5 -12.5,0 5,5 0,0 z\" id=\"path3947\" inkscape:connector-curvature=\"0\" style=\"fill-rule:evenodd;stroke:#000000;stroke-width:1pt\" transform=\"matrix(-0.8,0,0,-0.8,-10,0)\"/>\n",
        "    </marker>\n",
        "    <marker id=\"Arrow1Mstart\" inkscape:stockid=\"Arrow1Mstart\" orient=\"auto\" refX=\"0\" refY=\"0\" style=\"overflow:visible\">\n",
        "      <path d=\"M 0,0 5,-5 -12.5,0 5,5 0,0 z\" id=\"path3950\" inkscape:connector-curvature=\"0\" style=\"fill-rule:evenodd;stroke:#000000;stroke-width:1pt\" transform=\"matrix(0.4,0,0,0.4,4,0)\"/>\n",
        "    </marker>\n",
        "    <marker id=\"Arrow2Mstart\" inkscape:stockid=\"Arrow2Mstart\" orient=\"auto\" refX=\"0\" refY=\"0\" style=\"overflow:visible\">\n",
        "      <path d=\"M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z\" id=\"path3968\" inkscape:connector-curvature=\"0\" style=\"fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round\" transform=\"scale(0.6,0.6)\"/>\n",
        "    </marker>\n",
        "    <marker id=\"Arrow2Lend\" inkscape:stockid=\"Arrow2Lend\" orient=\"auto\" refX=\"0\" refY=\"0\" style=\"overflow:visible\">\n",
        "      <path d=\"M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z\" id=\"path3965\" inkscape:connector-curvature=\"0\" style=\"fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round\" transform=\"matrix(-1.1,0,0,-1.1,-1.1,0)\"/>\n",
        "    </marker>\n",
        "    <marker id=\"Arrow2Lstart\" inkscape:stockid=\"Arrow2Lstart\" orient=\"auto\" refX=\"0\" refY=\"0\" style=\"overflow:visible\">\n",
        "      <path d=\"M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z\" id=\"path3962\" inkscape:connector-curvature=\"0\" style=\"fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round\" transform=\"matrix(1.1,0,0,1.1,1.1,0)\"/>\n",
        "    </marker>\n",
        "    <marker id=\"Arrow1Lstart\" inkscape:stockid=\"Arrow1Lstart\" orient=\"auto\" refX=\"0\" refY=\"0\" style=\"overflow:visible\">\n",
        "      <path d=\"M 0,0 5,-5 -12.5,0 5,5 0,0 z\" id=\"path3944\" inkscape:connector-curvature=\"0\" style=\"fill-rule:evenodd;stroke:#000000;stroke-width:1pt\" transform=\"matrix(0.8,0,0,0.8,10,0)\"/>\n",
        "    </marker>\n",
        "    <marker id=\"Arrow2Lstart-9\" inkscape:stockid=\"Arrow2Lstart\" orient=\"auto\" refX=\"0\" refY=\"0\" style=\"overflow:visible\">\n",
        "      <path d=\"M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z\" id=\"path3962-7\" inkscape:connector-curvature=\"0\" style=\"fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round\" transform=\"matrix(1.1,0,0,1.1,1.1,0)\"/>\n",
        "    </marker>\n",
        "    <marker id=\"Arrow2Lstart-93\" inkscape:stockid=\"Arrow2Lstart\" orient=\"auto\" refX=\"0\" refY=\"0\" style=\"overflow:visible\">\n",
        "      <path d=\"M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z\" id=\"path3962-1\" inkscape:connector-curvature=\"0\" style=\"fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round\" transform=\"matrix(1.1,0,0,1.1,1.1,0)\"/>\n",
        "    </marker>\n",
        "    <marker id=\"Arrow1Lend3\" inkscape:stockid=\"Arrow1Lend3\" orient=\"auto\" refX=\"0\" refY=\"0\" style=\"overflow:visible\">\n",
        "      <path d=\"M 0,0 5,-5 -12.5,0 5,5 0,0 z\" id=\"path6018\" inkscape:connector-curvature=\"0\" style=\"fill:#008000;fill-rule:evenodd;stroke:#008000;stroke-width:1pt\" transform=\"matrix(-0.8,0,0,-0.8,-10,0)\"/>\n",
        "    </marker>\n",
        "    <marker id=\"Arrow2Lstart-0\" inkscape:stockid=\"Arrow2Lstart\" orient=\"auto\" refX=\"0\" refY=\"0\" style=\"overflow:visible\">\n",
        "      <path d=\"M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z\" id=\"path3962-2\" inkscape:connector-curvature=\"0\" style=\"fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round\" transform=\"matrix(1.1,0,0,1.1,1.1,0)\"/>\n",
        "    </marker>\n",
        "    <marker id=\"marker3091\" inkscape:stockid=\"Arrow2Lstart\" orient=\"auto\" refX=\"0\" refY=\"0\" style=\"overflow:visible\">\n",
        "      <path d=\"M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z\" id=\"path3093\" inkscape:connector-curvature=\"0\" style=\"fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round\" transform=\"matrix(1.1,0,0,1.1,1.1,0)\"/>\n",
        "    </marker>\n",
        "    <marker id=\"Arrow1Mstart-0\" inkscape:stockid=\"Arrow1Mstart\" orient=\"auto\" refX=\"0\" refY=\"0\" style=\"overflow:visible\">\n",
        "      <path d=\"M 0,0 5,-5 -12.5,0 5,5 0,0 z\" id=\"path3950-4\" inkscape:connector-curvature=\"0\" style=\"fill-rule:evenodd;stroke:#000000;stroke-width:1pt\" transform=\"matrix(0.4,0,0,0.4,4,0)\"/>\n",
        "    </marker>\n",
        "    <marker id=\"Arrow1Lend3-5\" inkscape:stockid=\"Arrow1Lend3\" orient=\"auto\" refX=\"0\" refY=\"0\" style=\"overflow:visible\">\n",
        "      <path d=\"M 0,0 5,-5 -12.5,0 5,5 0,0 z\" id=\"path6018-5\" inkscape:connector-curvature=\"0\" style=\"fill:#008000;fill-rule:evenodd;stroke:#008000;stroke-width:1pt\" transform=\"matrix(-0.8,0,0,-0.8,-10,0)\"/>\n",
        "    </marker>\n",
        "    <marker id=\"Arrow1Lend3-2\" inkscape:stockid=\"Arrow1Lend3\" orient=\"auto\" refX=\"0\" refY=\"0\" style=\"overflow:visible\">\n",
        "      <path d=\"M 0,0 5,-5 -12.5,0 5,5 0,0 z\" id=\"path6018-0\" inkscape:connector-curvature=\"0\" style=\"fill:#008000;fill-rule:evenodd;stroke:#008000;stroke-width:1pt\" transform=\"matrix(-0.8,0,0,-0.8,-10,0)\"/>\n",
        "    </marker>\n",
        "  </defs>\n",
        "  <metadata id=\"metadata2991\">\n",
        "    <rdf:RDF>\n",
        "      <cc:Work rdf:about=\"\">\n",
        "        <dc:format>image/svg+xml</dc:format>\n",
        "        <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\"/>\n",
        "        <dc:title/>\n",
        "      </cc:Work>\n",
        "    </rdf:RDF>\n",
        "  </metadata>\n",
        "  <g id=\"layer1\" inkscape:groupmode=\"layer\" inkscape:label=\"Layer 1\" transform=\"translate(-105.83124,-208.02101)\">\n",
        "    <text id=\"text4633\" sodipodi:linespacing=\"125%\" style=\"font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans\" x=\"228.83009\" xml:space=\"preserve\" y=\"469.66122\"><tspan id=\"tspan4635\" sodipodi:role=\"line\" x=\"228.83009\" y=\"469.66122\"/></text>\n",
        "    <path d=\"m 446.06042,264.54267 c -1.53626,16.15593 -3.07252,32.31185 -4.60877,48.46778 -13.22332,-22.90345 -26.44663,-45.80691 -39.66994,-68.71035 14.75957,6.74754 29.51914,13.49504 44.27871,20.24257 z m -134.07579,31.38245 c 29.90069,-17.26318 59.80139,-34.52636 89.7021,-51.78953 13.28662,23.01312 26.57327,46.02626 39.8599,69.03938 -29.90071,17.26318 -59.80141,34.52635 -89.7021,51.78953 -13.28663,-23.01313 -26.57327,-46.02626 -39.8599,-69.03938 z\" id=\"path3002\" inkscape:connector-curvature=\"0\" style=\"fill:#007fff;fill-opacity:1;fill-rule:evenodd;stroke:none\"/>\n",
        "    <path d=\"M 327.61989,332.77901 C 446.35082,264.22967 446.62282,264.07263 446.62282,264.07263\" id=\"path3799-8\" inkscape:connector-curvature=\"0\" style=\"fill:none;stroke:#000000;stroke-width:0.53514546;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6.42174526, 0.53514543;stroke-dashoffset:0\"/>\n",
        "    <rect height=\"18.038532\" id=\"rect3779\" style=\"fill:#aa4400;stroke:none\" transform=\"matrix(0.8660254,-0.50000001,0.50000001,0.8660254,0,0)\" width=\"36.077057\" x=\"131.77338\" y=\"391.53778\"/>\n",
        "    <g id=\"g6939\">\n",
        "      <path d=\"m 340.59375,329.71875 0.96875,1 6.09375,-5.9375 -1,-1.03125 z m -12.15625,11.90625 1,1 6.0625,-5.9375 -1,-1.03125 z m -12.15625,11.90625 1,1 6.0625,-5.9375 -1,-1.03125 z m -12.15625,11.875 1,1.03125 6.0625,-5.96875 -0.96875,-1 z m -12.15625,11.90625 1,1.03125 6.09375,-5.96875 -1,-1 z m -12.125,11.90625 0.96875,1.03125 6.09375,-5.96875 -1,-1 z m -12.15625,11.90625 1,1 6.0625,-5.9375 -1,-1 z m -12.15625,11.90625 1,1 6.0625,-5.9375 -0.96875,-1.03125 z m -12.15625,11.90625 1,1 6.0625,-5.9375 -0.96875,-1.03125 z m -12.125,11.90625 0.96875,1 6.09375,-5.9375 -1,-1.03125 z m -12.15625,11.90625 1,1 6.0625,-5.96875 -1,-1 z m -12.15625,11.875 1,1.03125 6.0625,-5.96875 -1,-1 z m -12.15625,11.90625 1,1.03125 6.0625,-5.96875 -0.96875,-1 z m -12.15625,11.90625 1,1.03125 6.09375,-5.96875 -1,-1 z m -12.125,11.90625 0.96875,1 6.09375,-5.9375 -1,-1 z m -12.15625,11.90625 1,1 6.0625,-5.9375 -1,-1.03125 z m -12.15625,11.90625 1,1 6.0625,-5.9375 -0.96875,-1.03125 z\" id=\"path5605\" inkscape:connector-curvature=\"0\" style=\"font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.41732287;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans\"/>\n",
        "      <path d=\"m 342.47834,328.84696 -0.0824,8.01717 10.20686,-17.93562 -18.14163,9.83605 8.01717,0.0824 z\" id=\"path6945\" inkscape:connector-curvature=\"0\" style=\"fill:#008000;fill-rule:evenodd;stroke:#008000;stroke-width:1.1338583pt\"/>\n",
        "    </g>\n",
        "    <g id=\"g4062\" transform=\"matrix(0.73501461,0,0,0.73501461,14.413444,63.510397)\">\n",
        "      <g id=\"g4605-5\" style=\"stroke-width:1.65409982;stroke-miterlimit:4;stroke-dasharray:none\" transform=\"matrix(0.37102887,-0.21421362,0.21421362,0.37102887,311.71247,156.20929)\">\n",
        "        <path d=\"m 87.948938,470.91859 c 0,87.1346 0,87.1346 0,87.1346\" id=\"path3938-2\" inkscape:connector-curvature=\"0\" style=\"fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1.65409982;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Lstart)\"/>\n",
        "        <path d=\"m 175.63762,557.79673 c -87.1346,0 -87.1346,0 -87.1346,0\" id=\"path3938-4-6\" inkscape:connector-curvature=\"0\" style=\"fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1.65409982;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Lstart)\"/>\n",
        "      </g>\n",
        "      <text id=\"text4609-9\" sodipodi:linespacing=\"125%\" style=\"font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans\" transform=\"matrix(0.8660254,-0.5,0.5,0.8660254,0,0)\" x=\"233.69742\" xml:space=\"preserve\" y=\"495.58575\"><tspan id=\"tspan4611-4\" sodipodi:role=\"line\" x=\"233.69742\" y=\"495.58575\">R<tspan id=\"tspan3171\" style=\"font-size:13.00018311px;baseline-shift:sub\">y</tspan></tspan></text>\n",
        "      <text id=\"text4615-1\" sodipodi:linespacing=\"125%\" style=\"font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans\" transform=\"matrix(0.8660254,-0.5,0.5,0.8660254,0,0)\" x=\"259.37842\" xml:space=\"preserve\" y=\"520.45239\"><tspan id=\"tspan4627-0\" sodipodi:role=\"line\" x=\"259.37842\" y=\"520.45239\">R<tspan id=\"tspan3173\" style=\"font-size:13.00018311px;baseline-shift:sub\">x</tspan></tspan></text>\n",
        "      <text id=\"text4621-5\" sodipodi:linespacing=\"125%\" style=\"font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans\" transform=\"matrix(0.8660254,-0.5,0.5,0.8660254,0,0)\" x=\"196.67409\" xml:space=\"preserve\" y=\"521.10706\"><tspan id=\"tspan4623-6\" sodipodi:role=\"line\" x=\"196.67409\" y=\"521.10706\">R<tspan id=\"tspan3169\" style=\"font-size:13.00018311px;baseline-shift:sub\">z</tspan></tspan></text>\n",
        "      <path d=\"m 78.934423,431.0819 c -11.914018,-3.71487 -18.26549,-15.52225 -14.186409,-26.37252 4.079081,-10.85026 17.044048,-16.63464 28.958066,-12.91976 6.85231,2.1366 12.18247,7.11093 14.36042,13.40177\" id=\"path4641-3\" sodipodi:cx=\"86.320251\" sodipodi:cy=\"411.43576\" sodipodi:end=\"5.9777527\" sodipodi:open=\"true\" sodipodi:rx=\"22.801577\" sodipodi:ry=\"20.76572\" sodipodi:start=\"1.9006636\" sodipodi:type=\"arc\" style=\"fill:none;stroke:#000000;stroke-width:2.51632953;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Mstart);marker-end:none\" transform=\"matrix(0.48778887,-0.28162504,0.28162504,0.48778887,306.0273,164.72779)\"/>\n",
        "    </g>\n",
        "    <rect height=\"18.038532\" id=\"rect3779-0\" style=\"fill:#aa4400;stroke:none\" transform=\"matrix(0.8660254,-0.50000001,0.50000001,0.8660254,0,0)\" width=\"36.077057\" x=\"130.3158\" y=\"494.18896\"/>\n",
        "    <path d=\"m 320.55777,257.3898 c 68.54934,118.73093 68.70638,119.00293 68.70638,119.00293\" id=\"path3799\" inkscape:connector-curvature=\"0\" style=\"fill:none;stroke:#000000;stroke-width:1.04175305;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6.25051811, 1.04175302;stroke-dashoffset:0\"/>\n",
        "    <path d=\"m 340.18453,384.98209 c 57.43196,-33.15834 57.56353,-33.23431 57.56353,-33.23431\" id=\"path3799-8-8-6\" inkscape:connector-curvature=\"0\" style=\"fill:none;stroke:#000000;stroke-width:1.04175305;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6.25051811, 1.04175302;stroke-dashoffset:0\"/>\n",
        "    <path d=\"m 289.75863,295.42888 c 57.43195,-33.15834 57.56353,-33.23431 57.56353,-33.23431\" id=\"path3799-8-8\" inkscape:connector-curvature=\"0\" style=\"fill:none;stroke:#000000;stroke-width:1.04175305;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6.25051811, 1.04175302;stroke-dashoffset:0\"/>\n",
        "    <g id=\"g3064\" style=\"stroke-width:1.92829204;stroke-miterlimit:4;stroke-dasharray:none\" transform=\"matrix(0.73501461,0,0,0.73501461,14.413444,63.510397)\">\n",
        "      <g id=\"g4605\" style=\"stroke-width:1.92829204;stroke-miterlimit:4;stroke-dasharray:none\" transform=\"translate(91.206306,65.147361)\">\n",
        "        <path d=\"m 87.948938,470.91859 c 0,87.1346 0,87.1346 0,87.1346\" id=\"path3938\" inkscape:connector-curvature=\"0\" style=\"fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1.92829204;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Lstart)\"/>\n",
        "        <path d=\"m 175.63762,557.79673 c -87.1346,0 -87.1346,0 -87.1346,0\" id=\"path3938-4\" inkscape:connector-curvature=\"0\" style=\"fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1.92829204;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Lstart)\"/>\n",
        "      </g>\n",
        "      <text id=\"text4609\" sodipodi:linespacing=\"125%\" style=\"font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans\" x=\"171.82617\" xml:space=\"preserve\" y=\"526.66516\"><tspan id=\"tspan4611\" sodipodi:role=\"line\" x=\"171.82617\" y=\"526.66516\">N<tspan id=\"tspan4631\" style=\"font-size:65.00091553%;baseline-shift:sub\">y</tspan></tspan></text>\n",
        "      <text id=\"text4615\" sodipodi:linespacing=\"125%\" style=\"font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans\" x=\"254.07471\" xml:space=\"preserve\" y=\"610.54236\"><tspan id=\"tspan4627\" sodipodi:role=\"line\" x=\"254.07471\" y=\"610.54236\">N<tspan id=\"tspan4629\" style=\"font-size:65.00091553%;baseline-shift:sub\">x</tspan></tspan></text>\n",
        "      <text id=\"text4621\" sodipodi:linespacing=\"125%\" style=\"font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans\" x=\"141.69551\" xml:space=\"preserve\" y=\"626.01489\"><tspan id=\"tspan4623\" sodipodi:role=\"line\" x=\"141.69551\" y=\"626.01489\">N<tspan id=\"tspan4637\" style=\"font-size:65.00091553%;baseline-shift:sub\">z</tspan></tspan></text>\n",
        "      <path d=\"m 78.934423,431.0819 c -11.914018,-3.71487 -18.26549,-15.52225 -14.186409,-26.37252 4.079081,-10.85026 17.044048,-16.63464 28.958066,-12.91976 6.85231,2.1366 12.18247,7.11093 14.36042,13.40177\" id=\"path4641\" sodipodi:cx=\"86.320251\" sodipodi:cy=\"411.43576\" sodipodi:end=\"5.9777527\" sodipodi:open=\"true\" sodipodi:rx=\"22.801577\" sodipodi:ry=\"20.76572\" sodipodi:start=\"1.9006636\" sodipodi:type=\"arc\" style=\"fill:none;stroke:#000000;stroke-width:3.4235096;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Mstart);marker-end:none\" transform=\"matrix(0.56325007,0,0,0.56325007,131.75665,389.01942)\"/>\n",
        "    </g>\n",
        "    <g id=\"g6947\">\n",
        "      <path d=\"m 542.625,316.53125 1.53125,0 -0.0312,-1.40625 -1.53125,0 z m -17.03125,-1.28125 0,1.40625 8.53125,-0.0625 -0.0312,-1.40625 z m -17,0.125 0,1.40625 8.5,-0.0625 0,-1.40625 z m -17,0.125 0,1.40625 8.5,-0.0625 0,-1.40625 z m -17.03125,0.0937 0.0312,1.4375 8.5,-0.0625 0,-1.40625 z m -17,0.125 0.0312,1.4375 8.5,-0.0625 -0.0312,-1.4375 z m -17,0.125 0,1.40625 8.5,-0.0312 0,-1.4375 z m -17,0.125 0,1.40625 8.5,-0.0625 0,-1.40625 z m -17.03125,0.125 0.0312,1.40625 8.5,-0.0625 0,-1.40625 z m -17,0.125 0.0312,1.40625 8.5,-0.0625 -0.0312,-1.40625 z m -17,0.0937 0,1.4375 8.5,-0.0625 0,-1.4375 z m -17,0.125 0,1.4375 8.5,-0.0625 0,-1.4375 z\" id=\"path5605-4\" inkscape:connector-curvature=\"0\" style=\"font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.41732287;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans\"/>\n",
        "      <path d=\"m 529.96816,315.92575 -5.62925,5.70906 19.80213,-5.80881 -19.88193,-5.52949 5.70905,5.62924 z\" id=\"path6953\" inkscape:connector-curvature=\"0\" style=\"fill:#008000;fill-rule:evenodd;stroke:#008000;stroke-width:1.1338583pt\"/>\n",
        "    </g>\n",
        "    <g id=\"g6955\">\n",
        "      <path d=\"m 514.21875,223.0625 0.71875,1.21875 1.3125,-0.78125 -0.71875,-1.21875 z m -14.6875,8.625 0.71875,1.21875 7.34375,-4.3125 -0.71875,-1.21875 z m -14.65625,8.59375 0.71875,1.21875 7.34375,-4.3125 -0.71875,-1.21875 z m -14.65625,8.59375 0.6875,1.25 7.34375,-4.3125 -0.71875,-1.21875 z m -14.6875,8.625 0.71875,1.21875 7.34375,-4.3125 -0.71875,-1.21875 z m -14.65625,8.59375 0.71875,1.21875 7.3125,-4.28125 -0.71875,-1.21875 z m -14.6875,8.625 0.71875,1.21875 7.34375,-4.3125 -0.71875,-1.21875 z M 411.53125,283.3125 412.25,284.53125 419.59375,280.25 418.875,279 z m -14.6875,8.625 0.71875,1.21875 7.34375,-4.3125 -0.71875,-1.21875 z m -14.65625,8.59375 0.71875,1.21875 7.34375,-4.3125 -0.71875,-1.21875 z m -14.65625,8.59375 0.71875,1.25 7.3125,-4.3125 -0.71875,-1.21875 z m -14.6875,8.625 0.71875,1.21875 7.34375,-4.3125 -0.71875,-1.21875 z\" id=\"path5605-4-2\" inkscape:connector-curvature=\"0\" style=\"font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.41732287;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans\"/>\n",
        "      <path d=\"m 503.66462,230.07572 -2.02055,7.75881 14.24474,-14.93164 -19.983,5.15228 7.75881,2.02055 z\" id=\"path6961\" inkscape:connector-curvature=\"0\" style=\"fill:#008000;fill-rule:evenodd;stroke:#008000;stroke-width:1.1338583pt\"/>\n",
        "    </g>\n",
        "    <text id=\"text4138\" sodipodi:linespacing=\"125%\" style=\"font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans\" x=\"496.06177\" xml:space=\"preserve\" y=\"284.53506\"><tspan id=\"tspan4142\" sodipodi:role=\"line\" x=\"496.06177\" y=\"284.53506\">\u03b8</tspan></text>\n",
        "    <path d=\"m 268.12559,317.98268 a 87.525665,87.525665 0 0 1 11.72622,43.76283\" id=\"path4162\" sodipodi:cx=\"192.32614\" sodipodi:cy=\"361.74551\" sodipodi:end=\"6.2831853\" sodipodi:open=\"true\" sodipodi:rx=\"87.525665\" sodipodi:ry=\"87.525665\" sodipodi:start=\"5.7595865\" sodipodi:type=\"arc\" style=\"fill:none;stroke:#000000;stroke-width:0.7455861;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;marker-start:url(#marker3091);marker-end:url(#Arrow2Lend)\" transform=\"matrix(1.3972269,0,0,1.3972269,102.15609,-193.71812)\"/>\n",
        "    <g id=\"g6973\">\n",
        "      <path d=\"m 294.9375,296.4375 -0.90625,0.53125 47.5625,82.375 0.90625,-0.53125 -47.5625,-82.375 z\" id=\"path3799-6\" inkscape:connector-curvature=\"0\" style=\"font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.0629921;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans\"/>\n",
        "      <path d=\"m 296.08321,308.8962 -2.31927,-13.41288 10.45625,8.71499 c -3.42253,-0.38074 -6.70305,1.52685 -8.13698,4.69789 z\" id=\"path6979\" inkscape:connector-curvature=\"0\" style=\"fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round\"/>\n",
        "      <path d=\"m 340.45634,366.8871 2.31977,13.4128 -10.45658,-8.71462 c 3.42255,0.38062 6.703,-1.52709 8.13681,-4.69818 z\" id=\"path6981\" inkscape:connector-curvature=\"0\" style=\"fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round\"/>\n",
        "    </g>\n",
        "    <text id=\"text5846\" sodipodi:linespacing=\"125%\" style=\"font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans\" transform=\"matrix(0.8660254,-0.50000001,0.50000001,0.8660254,0,0)\" x=\"74.986252\" xml:space=\"preserve\" y=\"439.57254\"><tspan id=\"tspan5848\" sodipodi:role=\"line\" style=\"font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans\" x=\"74.986252\" y=\"439.57254\">2w</tspan></text>\n",
        "    <rect height=\"82.919052\" id=\"rect5896\" style=\"fill:#007fff;fill-opacity:1;stroke:#000000;stroke-width:1.0629921;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0\" width=\"188.87119\" x=\"624.88025\" y=\"389.38519\"/>\n",
        "    <rect height=\"82.919052\" id=\"rect5896-2\" style=\"fill:#007fff;fill-opacity:1;stroke:#000000;stroke-width:1.0629921;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0\" width=\"67.244766\" x=\"814.6001\" y=\"389.38519\"/>\n",
        "    <path d=\"m 604.61811,505.12637 a 50.096928,50.096928 0 1 1 -100.19386,0 50.096928,50.096928 0 1 1 100.19386,0 z\" id=\"path5962\" sodipodi:cx=\"554.52118\" sodipodi:cy=\"505.12637\" sodipodi:rx=\"50.096928\" sodipodi:ry=\"50.096928\" sodipodi:type=\"arc\" style=\"fill:#aa4400;fill-opacity:1;stroke:#000000;stroke-width:1.0629921;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0\" transform=\"translate(137.42757,-34.549606)\"/>\n",
        "    <path d=\"m 604.61811,505.12637 a 50.096928,50.096928 0 1 1 -100.19386,0 50.096928,50.096928 0 1 1 100.19386,0 z\" id=\"path5962-8\" sodipodi:cx=\"554.52118\" sodipodi:cy=\"505.12637\" sodipodi:rx=\"50.096928\" sodipodi:ry=\"50.096928\" sodipodi:type=\"arc\" style=\"fill:#aa4400;fill-opacity:1;stroke:#000000;stroke-width:1.0629921;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0\" transform=\"matrix(0.46544142,0,0,0.46544142,592.90296,260.8064)\"/>\n",
        "    <path d=\"m 587.62548,520.67368 c 334.17283,0 334.17283,0 334.17283,0\" id=\"path5984\" inkscape:connector-curvature=\"0\" style=\"fill:none;stroke:#000000;stroke-width:1.21631289;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none\"/>\n",
        "    <text id=\"text5986\" sodipodi:linespacing=\"125%\" style=\"font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans\" x=\"245.60551\" xml:space=\"preserve\" y=\"444.42282\"><tspan id=\"tspan5988\" sodipodi:role=\"line\" style=\"font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans\" x=\"245.60551\" y=\"444.42282\">d=[x y]<tspan id=\"tspan5990\" style=\"font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;baseline-shift:super;font-family:Sans;-inkscape-font-specification:Sans\">T</tspan></tspan></text>\n",
        "    <path d=\"m 331.67622,602.4411 a 10.364882,10.364882 0 1 1 -20.72976,0 10.364882,10.364882 0 1 1 20.72976,0 z\" id=\"path5992\" sodipodi:cx=\"321.31134\" sodipodi:cy=\"602.4411\" sodipodi:rx=\"10.364882\" sodipodi:ry=\"10.364882\" sodipodi:type=\"arc\" style=\"fill:#000000;stroke:#000000;stroke-width:1.41732287;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0\" transform=\"matrix(0.5839982,0,0,0.5839982,502.74027,119.87312)\"/>\n",
        "    <path d=\"m 581.39239,410.69079 a 58.734329,58.734329 0 0 1 101.73084,0\" id=\"path6000\" sodipodi:cx=\"632.25781\" sodipodi:cy=\"440.05795\" sodipodi:end=\"5.7595865\" sodipodi:open=\"true\" sodipodi:rx=\"58.734329\" sodipodi:ry=\"58.734329\" sodipodi:start=\"3.6651914\" sodipodi:type=\"arc\" style=\"fill:none;stroke:#000000;stroke-width:1.41732287;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker-start:none;marker-end:url(#Arrow2Lend)\" transform=\"translate(61.340945,20.729763)\"/>\n",
        "    <text id=\"text6418\" sodipodi:linespacing=\"125%\" style=\"font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans\" x=\"672.44708\" xml:space=\"preserve\" y=\"453.27121\"><tspan id=\"tspan6420\" sodipodi:role=\"line\" style=\"font-size:20px\" x=\"672.44708\" y=\"453.27121\">r</tspan></text>\n",
        "    <text id=\"text6422\" sodipodi:linespacing=\"125%\" style=\"font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans\" x=\"749.72644\" xml:space=\"preserve\" y=\"417.02487\"><tspan id=\"tspan6424\" sodipodi:role=\"line\" style=\"font-size:20px\" x=\"749.72644\" y=\"417.02487\">\u03d5<tspan id=\"tspan6428\" style=\"font-size:65.00091553%;baseline-shift:sub\">1</tspan></tspan></text>\n",
        "    <g id=\"g6963\">\n",
        "      <path d=\"m 649.3125,447.09375 -0.65625,1.21875 45.46875,24.40625 0.65625,-1.1875 -45.46875,-24.4375 z\" id=\"path6846\" inkscape:connector-curvature=\"0\" style=\"font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.35797536;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans\"/>\n",
        "      <path d=\"m 689.6615,469.54468 -3.67768,1.1079 9.65965,2.10434 -7.08986,-6.88992 1.10789,3.67768 z\" id=\"path6969\" inkscape:connector-curvature=\"0\" style=\"fill-rule:evenodd;stroke:#000000;stroke-width:0.54319015pt\"/>\n",
        "      <path d=\"m 653.77193,450.27248 3.67768,-1.1079 -9.65965,-2.10434 7.08986,6.88992 -1.10789,-3.67768 z\" id=\"path6971\" inkscape:connector-curvature=\"0\" style=\"fill-rule:evenodd;stroke:#000000;stroke-width:0.54319015pt\"/>\n",
        "    </g>\n",
        "  </g>\n",
        "</svg>"
       ],
       "text": [
        "<IPython.core.display.SVG at 0x3b426d0>"
       ]
      }
     ],
     "prompt_number": 1
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "## Equations of Motion\n",
      "\n",
      "We\u2019ll start by generating the equations of motion for the robot with `sympy.physics.mechanics`. The `mechanics` module provides a clean, intuitive workflow for deriving the equations of motion for multibody dynamic systems using either `KanesMethod` or `LagrangesMethod` objects. In this example we'll make use of the `LagrangesMethod` object, which generates the equations of motion using [Lagrangian Mechanics](http://en.wikipedia.org/wiki/Lagrangian_mechanics) with constraints and generalized forces. \n",
      "\n",
      "First we import the necessary functionality from SymPy."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "from sympy.physics.mechanics import *\n",
      "from sympy import sin, cos, symbols, Matrix, solve"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 2
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Next we need to define an inertial reference frame, as well as a coordinate origin. All coordinates and reference frames defined later will be in reference to the world coordinate frame."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Inertial Reference Frame\n",
      "N = ReferenceFrame('N')\n",
      "\n",
      "#Define a world coordinate origin\n",
      "O = Point('O')\n",
      "O.set_vel(N, 0)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 3
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Lagrange's Method requires a state vector $\\vec{q}$ that contains all the generalized coordinates required to define the system in space. In the case of the robot:\n",
      "\n",
      "$$\n",
      "\\vec{q} = [\\matrix{x && y && \\theta && \\phi_1 && \\phi_2}]^T\n",
      "$$\n",
      "\n",
      "As these are all time varying variables, they need to be created as `dynamicsymbols`:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "theta = dynamicsymbols('theta')                      #Rotation about N.z\n",
      "x, y = dynamicsymbols('x, y')                        #Coordinates of robot in World Frame\n",
      "phi1, phi2 = dynamicsymbols('phi_1, phi_2')          #Angular displacement of wheel\n",
      "\n",
      "#Create q and dq vectors\n",
      "q = Matrix([x, y, theta, phi1, phi2])\n",
      "dq = q.diff()"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 4
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "We'll also define some constant parameters that define the specific robot:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Constants for the wheels\n",
      "r = symbols('r')                                     #Radius of wheel\n",
      "m_w = symbols('m_w')                                 #Mass of the wheel\n",
      "t_w = symbols('t_w')                                 #Thickness of the wheel\n",
      "\n",
      "#Constants for the Robot Body\n",
      "w = symbols('w')                                     #2*w is the width of the wheel base\n",
      "d = symbols('d')                                     #Distance between axel and center of mass\n",
      "m_b = symbols('m_b')                                 #Mass of the body\n",
      "Ixx, Iyy, Izz = symbols('Ixx, Iyy, Izz')             #Moments of inertia of body"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 5
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Next we need to define a reference frame relative to the robot, and a point located at the center of the wheel axis. This will define the robot's location in the world coordinate frame. The velocity of this point is just the derivative of its x and y coordinates."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Robot Reference Frame\n",
      "R = N.orientnew('R', 'Axis', [theta, N.z])\n",
      "\n",
      "#Center of wheel base\n",
      "Cw = O.locatenew('Cw', x*N.x + y*N.y)\n",
      "\n",
      "#Set the velocity of point Cw\n",
      "Cw.set_vel(N, x.diff()*N.x + y.diff()*N.y)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 6
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Coordinates of the 2 wheel hubs then need to be specified. Luckily sympy mechanics makes this easy to do with the `locatenew` method. The hubs 1 and 2 are located at $-w\\vec{R_y}$ and $w\\vec{R_y}$ away from the center of the wheel base $C_w$ respectively.\n",
      "\n",
      "The velocity of the hubs is then found using the `v2pt_theory` method, which calculates the velocity of a point based off the velocity of another point in the same frame, and the angular velocity of that frame ([reference](http://en.wikipedia.org/wiki/Rotating_reference_frame#Relation_between_velocities_in_the_two_frames))."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Points at wheel hubs\n",
      "H1 = Cw.locatenew('H1', -w*R.y)\n",
      "H2 = Cw.locatenew('H2', w*R.y)\n",
      "\n",
      "#Set the velocity of points H1 and H2\n",
      "H1.v2pt_theory(Cw, N, R)\n",
      "H2.v2pt_theory(Cw, N, R);"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 7
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Rotating reference frames fixed to each wheel are then created. These rotate about the $\\vec{R_y}$ axis with the angular position of the wheels, $\\phi_1$ and $\\phi_2$."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Create reference frames for wheels 1 and 2\n",
      "W1 = R.orientnew('W1', 'Axis', [phi1, R.y])\n",
      "W2 = R.orientnew('W2', 'Axis', [phi2, R.y])"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 8
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "`sympy.physics.mechanics` provides classes for rigid bodies. These make calculating the lagrangian of a multibody system easier, as each body can be handled separately. To do this, each body needs the following things specified:\n",
      "\n",
      "- A name\n",
      "- A center of mass\n",
      "- A reference frame\n",
      "- A mass\n",
      "- A tuple of (Inertia, Point of rotation)\n",
      "\n",
      "Modeling the wheels as solid cylinders, the inertia can be calculated about the hubs, and `RigidBody` objects can be created for each wheel."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Calculate inertia of the wheel\n",
      "Iw = inertia(R, m_w*(3*r**2 + t_w**2)/12, m_w*r**2/2, m_w*(3*r**2 + t_w**2)/12)\n",
      "\n",
      "#Create rigid bodies for wheels\n",
      "Wheel1 = RigidBody('Wheel1', H1, W1, m_w, (Iw, H1))\n",
      "Wheel2 = RigidBody('Wheel2', H2, W2, m_w, (Iw, H2))"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 9
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "The same can be done for the body of the robot. In this case we model the body as a symmetric object with inertias $I_{xx}$, $I_{yy}$, and $I_{zz}$. The center of mass of the body is located a distance $d$ in front of the wheel axel."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Calculate inertia of body\n",
      "Ib = inertia(R, Ixx, Iyy, Izz)\n",
      "\n",
      "#Center of mass of body\n",
      "Cm = Cw.locatenew('Cm', d*R.x)\n",
      "Cm.v2pt_theory(Cw, N, R)\n",
      "\n",
      "#Create a rigid body object for body\n",
      "Body = RigidBody('Body', Cm, R, m_b, (Ib, Cm))"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 10
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "## Nonholonomic Constraints\n",
      "At this point, the lagrangian of the system can be calculated. However, the system still isn't fully defined; the constraints still need to be specificed.\n",
      "\n",
      "At this instance in time, the velocity of the ground contact points must have a velocity of zero. To specify this in the lagrange equations, a constraint matrix needs to be created.\n",
      "\n",
      "First, two points are created where the wheels contact the ground, and their velocities calculated."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Create two points, where the wheels contact the ground\n",
      "C1 = H1.locatenew('C1', -r*R.z)\n",
      "C2 = H2.locatenew('C2', -r*R.z)\n",
      "#Calculate velocity of points\n",
      "C1.v2pt_theory(H1, N, W1)\n",
      "C2.v2pt_theory(H2, N, W2);"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 11
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Using these velocities, a system of equations can be created. As the velocities must be zero, this expression = 0."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Express the velocity of points in the inertial frame\n",
      "con1 = C1.vel(N).express(N).args[0][0]\n",
      "con2 = C2.vel(N).express(N).args[0][0]\n",
      "#Create a matrix of constraints\n",
      "constraints = con1.col_join(con2)\n",
      "mprint(constraints)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Matrix([\n",
        "[(-r*phi_1' + w*theta')*cos(theta) + x'],\n",
        "[(-r*phi_1' + w*theta')*sin(theta) + y'],\n",
        "[                                     0],\n",
        "[(-r*phi_2' - w*theta')*cos(theta) + x'],\n",
        "[(-r*phi_2' - w*theta')*sin(theta) + y'],\n",
        "[                                     0]])\n"
       ]
      }
     ],
     "prompt_number": 12
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "There are 6 equations with 3 unknowns ($\\dot{x}$, $\\dot{y}$, and $\\dot{theta}$), there are duplicate equations. Using solve, and reconfiguring the expression, this can be reduced to the required set:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Solve for dx, dy, and dtheta in terms of dphi1 and dphi2\n",
      "sol = solve(constraints, dq[:3])\n",
      "\n",
      "#Split the resulting dict into a rhs and lhs, that are equivalent\n",
      "sol_rhs = Matrix(sol.values())\n",
      "sol_lhs = Matrix(sol.keys())\n",
      "\n",
      "#Since sol_rhs = sol_lhs --> sol_rhs - sol_lhs = 0\n",
      "#This forms the basis of our constraint matrix.\n",
      "#Combining, and solving for a linear representation:\n",
      "c = (sol_rhs - sol_lhs).jacobian(dq[:5])\n",
      "mprint(c)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Matrix([\n",
        "[-1,  0,  0, r*cos(theta)/2, r*cos(theta)/2],\n",
        "[ 0,  0, -1,        r/(2*w),       -r/(2*w)],\n",
        "[ 0, -1,  0, r*sin(theta)/2, r*sin(theta)/2]])\n"
       ]
      }
     ],
     "prompt_number": 13
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "This is our constraint matrix. The `LagrangesMethod` class requires that the constraints be specified as\n",
      "\n",
      "`coneqs` = $ C\\dot{\\vec{q}} = 0 $\n",
      "\n",
      "Thus:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Constraint Equations\n",
      "coneqs = (c*dq)\n",
      "mprint(coneqs)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Matrix([\n",
        "[r*cos(theta)*phi_1'/2 + r*cos(theta)*phi_2'/2 - x'],\n",
        "[          r*phi_1'/(2*w) - r*phi_2'/(2*w) - theta'],\n",
        "[r*sin(theta)*phi_1'/2 + r*sin(theta)*phi_2'/2 - y']])\n"
       ]
      }
     ],
     "prompt_number": 14
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "## Generalized Forces\n",
      "\n",
      "The inputs of system are the torques at the wheels. Using the right-hand-rule, it can be seen that a positive torque will exert force on the system in the $R_x$ direction. With a wheel radius of $r$, the force on the wheel hubs is:\n",
      "\n",
      "$\\vec{F_{hub}} = \\tau*\\vec{R_x}$"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Define forces on system:\n",
      "T1, T2 = symbols('tau_1, tau_2')              #Torques from the wheels\n",
      "fl = [(H1, r*T1*R.x),\n",
      "      (H2, r*T2*R.x)]"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 15
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "## Solving the System Dynamics\n",
      "\n",
      "We are now ready to solve the for the equations of motion. First, calculate the lagrangian of the system $L = T - V$:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "Lag = Lagrangian(N, Wheel1, Wheel2, Body)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 16
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Next, a `LagrangesMethod` object is created. This takes the following parameters:\n",
      "\n",
      "- The system lagrangian\n",
      "- The generalized coordinate vector $\\vec{q}$\n",
      "- The constraint equations\n",
      "- A list force tuples: (Point, Force at point)\n",
      "- An inertial reference frame"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "lm = LagrangesMethod(Lag, q, coneqs=coneqs, forcelist=fl, frame=N)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 17
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "The equations of motion can then be found:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "le = lm.form_lagranges_equations()\n",
      "mprint(le)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "Matrix([\n",
        "[                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     m_b*(-2*d*sin(theta)*theta'' - 2*d*cos(theta)*theta'**2 + 2*x'')/2 + m_w*(-2*w*sin(theta)*theta'**2 + 2*w*cos(theta)*theta'' + 2*x'')/2 + m_w*(2*w*sin(theta)*theta'**2 - 2*w*cos(theta)*theta'' + 2*x'')/2 - r*tau_1*cos(theta) - r*tau_2*cos(theta) - lam1],\n",
        "[                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     m_b*(-2*d*sin(theta)*theta'**2 + 2*d*cos(theta)*theta'' + 2*y'')/2 + m_w*(-2*w*sin(theta)*theta'' - 2*w*cos(theta)*theta'**2 + 2*y'')/2 + m_w*(2*w*sin(theta)*theta'' + 2*w*cos(theta)*theta'**2 + 2*y'')/2 - r*tau_1*sin(theta) - r*tau_2*sin(theta) - lam3],\n",
        "[Izz*theta'' - m_b*(d*(-sin(theta)*y' - cos(theta)*x')*theta' - d*sin(theta)*theta'*y' - d*cos(theta)*theta'*x')/2 + m_b*(2*d**2*theta'' + d*(-sin(theta)*theta'*y' - sin(theta)*x'' - cos(theta)*theta'*x' + cos(theta)*y'') - d*sin(theta)*theta'*y' - d*sin(theta)*x'' - d*cos(theta)*theta'*x' + d*cos(theta)*y'')/2 + m_w*(3*r**2 + t_w**2)*theta''/6 - m_w*(-w*(-sin(theta)*x' + cos(theta)*y')*theta' + w*sin(theta)*theta'*x' - w*cos(theta)*theta'*y')/2 - m_w*(w*(-sin(theta)*x' + cos(theta)*y')*theta' - w*sin(theta)*theta'*x' + w*cos(theta)*theta'*y')/2 + m_w*(2*w**2*theta'' - w*(-sin(theta)*theta'*x' + sin(theta)*y'' + cos(theta)*theta'*y' + cos(theta)*x'') + w*sin(theta)*theta'*x' - w*sin(theta)*y'' - w*cos(theta)*theta'*y' - w*cos(theta)*x'')/2 + m_w*(2*w**2*theta'' + w*(-sin(theta)*theta'*x' + sin(theta)*y'' + cos(theta)*theta'*y' + cos(theta)*x'') - w*sin(theta)*theta'*x' + w*sin(theta)*y'' + w*cos(theta)*theta'*y' + w*cos(theta)*x'')/2 - r*tau_1*w + r*tau_2*w - lam2],\n",
        "[                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    m_w*r**2*phi_1''/2 + r*lam1*cos(theta)/2 + r*lam3*sin(theta)/2 + r*lam2/(2*w)],\n",
        "[                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    m_w*r**2*phi_2''/2 + r*lam1*cos(theta)/2 + r*lam3*sin(theta)/2 - r*lam2/(2*w)]])\n"
       ]
      }
     ],
     "prompt_number": 18
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Sympy makes this super easy: no need to do this by hand, and no more mistakes in algebra. However, these are rather complicated equations. Luckily, they can be converted into a function allowing numerical simulation."
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "## Simulation\n",
      "\n",
      "To gain insight into the system dynamics, numerical simulation will be used. First, we need to import a few more functions:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "from sympy import lambdify, Dummy\n",
      "from scipy import array, hstack, linspace, ones\n",
      "from scipy import random, interp, vstack\n",
      "from scipy.integrate import odeint\n",
      "import matplotlib.pyplot as plt\n",
      "%matplotlib inline"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 19
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "The vector $\\ddot{\\vec{q}}$ can be calculated in a closed form expression using the `rhs` method. This method takes quite a long time to solve entirely symbolically (it took over an hour on my machine), so a simplifying assumption will be made: $d = 0$. This is actually pretty reasonable, as the motors and battery will be the heaviest part, and should be situated around the axle."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Substitute in d = 0\n",
      "lm.eom = lm.eom.subs({d: 0})\n",
      "\n",
      "#Solve for the rhs:\n",
      "rhs = lm.rhs()"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 20
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "We're almost done, all we need to do is model the dynamics of the motor. In the future there may be dynamics classes for electronics, allowing a piecewise construction like we did for the mechanics (using `RigidBody`). Defining the motor dynamics using the current code-base isn't terribly tricky though.\n",
      "\n",
      "In general, we can model the current dynamics of the motor as follows:\n",
      "\n",
      "$$\n",
      "\\dot{i} = -K/L \\dot{\\phi} - R/L i + V/L\n",
      "$$\n",
      "\n",
      "$$\n",
      "\\tau = K i\n",
      "$$\n",
      "\n",
      "where $i$ and $V$ are the current and voltage through the motor, $K$ is the motor constant, $R$ is the coil resistance, and $L$ is the coil inductance. Creating this model in sympy is fairly straightforward:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Create dynamic symbols for current and voltage\n",
      "i_1, i_2 = dynamicsymbols('i_1, i_2')        #Currents through motor 1 and 2\n",
      "V_1, V_2 = symbols('V_1, V_2')               #Voltages across the motor terminals\n",
      "\n",
      "#Define some motor constants.\n",
      "#Assuming motor 1 and 2 are the same:\n",
      "R = symbols('R')                             #Coil resistance\n",
      "L = symbols('L')                             #Coil inductance\n",
      "K1, K2 = symbols('K1, K2')                   #Motor constant\n",
      "\n",
      "#Define the motor dynamics\n",
      "di = Matrix([[-K1/L*phi1.diff() - R/L*i_1 + V_1/L],\n",
      "             [-K2/L*phi2.diff() - R/L*i_2 + V_2/L]])"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 21
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Now we just need to combine the motor model with the model for the robot:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Define consts:\n",
      "params = [Izz,  t_w,  m_w,    r,  m_b,   w,    R,      L,   K1,  K2]\n",
      "values = [  5, 0.15,  2.0, 0.15, 50.0, 0.6, 0.05, 0.0001,  1.0, 1.0]       #Values of constants\n",
      "\n",
      "#Create a list of dynamic symbols for simulation\n",
      "dynamics = q.T.tolist()[0] + dq.T.tolist()[0] + lm.lam_vec.T.tolist()[0] + [i_1, i_2]\n",
      "\n",
      "#Set the inputs to be the motor voltages\n",
      "inputs = [V_1, V_2]\n",
      "\n",
      "#Add the motor model to the rhs equations\n",
      "aug_rhs = rhs.col_join(di)\n",
      "\n",
      "#Substitute in K*i for T in the rhs equations\n",
      "aug_rhs = aug_rhs.subs({T1: K1*i_1, T2: K2*i_2})"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 22
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "This can now be converted into a function to solve the rhs based on current state using `lambdify`: "
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Create a list of dynamic symbols for simulation\n",
      "dummys = [Dummy() for i in dynamics]\n",
      "dummydict = dict(zip(dynamics, dummys))\n",
      "#Sub in the dummy symbols\n",
      "rhs_dummy = aug_rhs.subs(dummydict)\n",
      "#Lambdify function\n",
      "rhs_func = lambdify(dummys + inputs + params, rhs_dummy)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 23
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Create a function in the required format for odeint\n",
      "def right_hand_side(x, t, ts, us, values):\n",
      "    \"\"\"Calculate the rhs of the integral, at\n",
      "    time t, state x.\n",
      "    ts, us are used to get the current input\n",
      "    values are constants in the integral\"\"\"\n",
      "    \n",
      "    #Interp is needed to get u for timestep\n",
      "    u1 = interp(t, ts, us[:,0])\n",
      "    u2 = interp(t, ts, us[:,1])\n",
      "    \n",
      "    arguments = hstack((x, u1, u2, values))\n",
      "    \n",
      "    #Need call to array and reshape, as odeint\n",
      "    #requires state vector to be 1d\n",
      "    dx = array(rhs_func(*arguments))\n",
      "    return dx.reshape(dx.size)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 24
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Now we have everything we need to simulate the motion. First, let's see what happens if we input a constant, unbalanced voltage signal. Motor 1 gets 24 volts, Motor 2 gets 20 volts. Intuitively, this should cause the robot to drive in a circle."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "ts = linspace(0, 30, 3000)\n",
      "us = vstack((24*ones(len(ts)), 20*ones(len(ts)))).T\n",
      "#Run the simulation\n",
      "x0 = [0,] * 15                         #Start out at origin, unmoving\n",
      "xs = odeint(right_hand_side, x0, ts, args=(ts, us, values))\n",
      "\n",
      "plt.plot(xs[:,5], xs[:,6])\n",
      "plt.title('Robot Position')\n",
      "plt.xlabel('x (m)')\n",
      "plt.ylabel('y (m)');"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "metadata": {},
       "output_type": "display_data",
       "png": "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\n/owxZoT+Hz+Z527QQzcPAAAAAElFTkSuQmCC\n",
       "text": [
        "<matplotlib.figure.Figure at 0x5fd57d0>"
       ]
      }
     ],
     "prompt_number": 25
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "As seen by the simulation, the results line up with our intuition. The reason it doesn't immediately start out in a circle is due to the time it takes for it to accelerate from rest.\n",
      "\n",
      "Next, lets see what happens when the input voltages are matched:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "ts = linspace(0, 10, 1000)\n",
      "us = 20*ones((len(ts),2))\n",
      "#Run the simulation\n",
      "x0 = [0,] * 15                         #Start out at origin, unmoving\n",
      "xs = odeint(right_hand_side, x0, ts, args=(ts, us, values))\n",
      "plt.plot(xs[:,0], xs[:,1])\n",
      "plt.title('Robot Position')\n",
      "plt.xlabel('x (m)')\n",
      "plt.ylabel('y (m)');\n",
      "plt.ylim(-2, 2);"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "metadata": {},
       "output_type": "display_data",
       "png": "iVBORw0KGgoAAAANSUhEUgAAAYwAAAEXCAYAAAC+mHPKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAHr1JREFUeJzt3XtwVPX9//HXycVLIOXOApvItiSUkIQkllvbiSyFQAEJ\nIJaLgBECZlBAOh0Ga7EGCxS81IHgOGiVmzeKUwRLSEFkwdGB1YqooHINJCFkDBdJkCEhfn5/+GO/\nhiTm5LokPB8zO7PnnM+e8/54ZF8553POWcsYYwQAQDUC/F0AAKBpIDAAALYQGAAAWwgMAIAtBAYA\nwBYCAwBgC4GBm47L5dLOnTv9XYZtoaGhys7OrnJ5TEyM9uzZ03gF4aZFYKBJcrlcCgkJUWhoqDp1\n6qQpU6bo4sWLtj5rWZYsy6rVdgMCAnT8+PEql69Zs0aBgYEKDQ1Vq1atlJCQoK1bt9ZqW9cUFRXJ\n5XJJkh544AE9/vjj5ZZ/8cUXuuuuu+q0DcAOAgNNkmVZ+s9//qOioiIdOHBAn3/+uRYtWtQo267u\nXtff/va3Kioq0oULF5Samqpx48bp22+/bZTagIZEYKDJczgcGjJkiA4ePOibt2XLFkVHR6tNmzYa\nOHCgvvrqq3Kf8Xq9io6OVtu2bTVt2jRduXLFt+yll15SZGSk2rVrp1GjRik/P1+SfH/Fx8XFKTQ0\nVBs3bqy0nmuBYlmWpk6dqsuXL+vYsWP69ttvdf/996tjx45yuVxavHixr+3Ro0c1YMAAtW7dWh06\ndNCECRN86wsICNCxY8f04osv6vXXX9dTTz2l0NBQjRo1SlL5U2xXrlzR3Llz5XQ65XQ69cc//lEl\nJSWSJI/Ho7CwMP3jH/+Qw+FQly5dtGbNmlr/d8fNh8BAk3XtyzY3N1dZWVnq16+fJOnw4cO67777\ntGLFChUWFmr48OEaOXKkrl696vvc66+/ru3bt+vYsWM6fPiw7+jkvffe02OPPaaNGzcqPz9fXbt2\n9X15Xxsn+Oyzz1RUVKQ//OEPP1nf1atX9c9//lOhoaGKiIjQ7NmzVVRUpBMnTmj37t1at26dVq9e\nLUl6/PHH9fvf/14XLlxQXl6e5syZU25dlmXpwQcf1KRJkzR//nwVFRVp8+bNvmXXTrEtXrxYXq9X\nBw4c0IEDB+T1essdeRUUFOjixYs6ffq0Xn75ZT388MMc/cA+AzRBXbt2NS1btjShoaHGsiwzevRo\nU1ZWZowx5sknnzTjx4/3tf3++++N0+k0u3fvNsYY43K5zKpVq3zLMzMzTbdu3YwxxkybNs3Mnz/f\nt6y4uNgEBwebkydPGmOMsSzLHDt2rMq6Vq9ebYKCgkzr1q1N+/btza9//Wuzc+dOc/XqVXPLLbeY\nL7/80td21apVxu12G2OMuf/++82DDz5ocnNzK6zzx9t84IEHzIIFC8otd7lcZufOncYYY7p162a2\nbdvmW/bf//7XuFwuY4wxu3btMrfffrvvv5MxxnTs2NHs27evyv4AP8YRBpoky7K0efNmXbx4UR6P\nR++9954+/vhjSVJ+fr7uuOOOcm3Dw8OVl5fnmxceHu57f8cdd+j06dO+z3bt2tW3rEWLFmrXrl25\nz1anf//+On/+vL755ht9+OGH+t3vfqfCwkKVlpaWW/cdd9zhW+9TTz0lY4z69u2rmJgY35FHTZ0+\nfbrCNq71TZLatWungID/+2cfEhKi4uLiWm0LNx8CA03eXXfdpdmzZ2v+/PmSpC5duujkyZO+5cYY\n5eTkyOl0+uadOnWq3Ptry7p06VLuEtZLly7p7Nmz5T5bG+3bt1dwcHC5dZ86dUphYWGSfhiHefHF\nF5WXl6dVq1bpoYceqvRqrOqu7rq+/lOnTqlLly51qh24hsBAszB37lx5vV7t27dP48aN09atW/Xe\ne++ptLRUzz77rG677Tb95je/kfRDgDz//PPKy8vTuXPntHjxYo0fP16SNHHiRK1evVoHDhzQlStX\n9Nhjj6l///6+IxaHw6Fjx47VuL7AwECNGzdOf/nLX1RcXKyTJ0/queee0+TJkyVJGzduVG5uriSp\ndevWsiyr3JHANQ6H4ycv6504caIWLVqkwsJCFRYW6sknn9SUKVNqXC9QGQIDzUL79u2VkpKiZcuW\nqXv37nr11Vc1e/ZsdejQQVu3btU777yjoKAgST/8lT5p0iQNGTJE3bp1U2RkpBYsWCBJGjRokP72\nt79p7Nix6tKli06cOKE333zTt5309HSlpKSoTZs2euuttyrU8VP3eGRkZKhFixb6xS9+ocTERE2a\nNEnTpk2TJH388cfq37+/7+qnFStW+O69+PH6UlNTdejQIbVp00b33HNPhW0sWLBAvXv3Vq9evdSr\nVy/17t3b17fr1wXUlGUMP6AEAKie344wcnJyNHDgQEVHRysmJkYrVqyotN2cOXMUGRmpuLg47d+/\nv5GrBABcE+SvDQcHB+u5555TfHy8iouL9atf/UpJSUmKiorytcnMzNTRo0d15MgR7du3TzNnztTe\nvXv9VTIA3NT8doTRqVMnxcfHS5JatmypqKiocpf/ST/crZuSkiJJ6tevny5cuKCCgoJGrxUA4Mcj\njB/Lzs7W/v37fXfqXpOXl1fuevmwsDDl5ubK4XD45jGIBwC1U9MhbL9fJVVcXKx7771Xy5cvV8uW\nLSssv75DlQWEMabZvp544gm/10Df6B/9a36v2vBrYJSWlmrs2LGaPHmyRo8eXWG50+lUTk6Obzo3\nN7fON1ABAGrHb4FhjFFqaqp69uypuXPnVtomOTlZ69atkyTt3btXrVu3Lnc6CgDQePw2hvHBBx/o\n1VdfVa9evZSQkCBJWrJkie+RDWlpaRo+fLgyMzMVERGhFi1a1Pr5Ok2Z2+32dwkNpjn3TaJ/TV1z\n719tNPkb9yzLqvX5OAC4WdXmu9Pvg94AgKaBwAAA2EJgAABsITAAALYQGAAAWwgMAIAtBAYAwBYC\nAwBgC4EBALCFwAAA2EJgAABsITAAALYQGAAAWwgMAIAtBAYAwBYCAwBgC4EBALCFwAAA2EJgAABs\n8VtgTJs2TQ6HQ7GxsZUu93g8atWqlRISEpSQkKBFixY1coUAgB8L8teGp06dqtmzZ+v++++vss2A\nAQO0ZcuWRqwKAFAVvx1hJCYmqk2bNj/ZxhjTSNUAAKrjtyOM6liWpQ8//FBxcXFyOp165pln1LNn\nz0rbpqen+9673W653e7GKRIAmgiPxyOPx1OndVjGj3/GZ2dna+TIkfr8888rLCsqKlJgYKBCQkK0\nbds2PfLIIzp8+HCFdpZlcSQCADVUm+/OG/YqqdDQUIWEhEiShg0bptLSUp07d87PVQHAzeuGDYyC\nggJf+nm9Xhlj1LZtWz9XBQA3L7+NYUycOFG7d+9WYWGhwsPDtXDhQpWWlkqS0tLS9NZbb+mFF15Q\nUFCQQkJC9Oabb/qrVACA/DyGUR8YwwCAmmtWYxgAgBsLgQEAsIXAAADYQmAAAGwhMAAAthAYAABb\nCAwAgC0EBgDAFgIDAGALgQEAsIXAAADYQmAAAGwhMAAAthAYAABbCAwAgC0EBgDAFgIDAGALgQEA\nsIXAAADY4rfAmDZtmhwOh2JjY6tsM2fOHEVGRiouLk779+9vxOoAANfzW2BMnTpVWVlZVS7PzMzU\n0aNHdeTIEb344ouaOXNmI1YHALhekL82nJiYqOzs7CqXb9myRSkpKZKkfv366cKFCyooKJDD4ajQ\nNj093ffe7XbL7XbXc7UA0LR5PB55PJ46rcNvgVGdvLw8hYeH+6bDwsKUm5tbbWAAACq6/o/phQsX\n1ngdN/SgtzGm3LRlWX6qBABwwwaG0+lUTk6Obzo3N1dOp9OPFQHAze2GDYzk5GStW7dOkrR37161\nbt260tNRAIDG4bcxjIkTJ2r37t0qLCxUeHi4Fi5cqNLSUklSWlqahg8frszMTEVERKhFixZavXq1\nv0oFAEiyzPUDBU2MZVkVxjoAAD+tNt+dN+wpKQDAjYXAAADYQmAAAGwhMAAAthAYAABbCAwAgC0E\nBgDAFgIDAGALgQEAsIXAAADYQmAAAGwhMAAAthAYAABbCAwAgC0EBgDAFgIDAGALgQEAsIXAAADY\n4tfAyMrKUo8ePRQZGally5ZVWO7xeNSqVSslJCQoISFBixYt8kOVAABJCvLXhsvKyjRr1iy9++67\ncjqd6tOnj5KTkxUVFVWu3YABA7RlyxY/VQkAuMZvRxher1cRERFyuVwKDg7WhAkTtHnz5grtavoj\n5QCAhuG3I4y8vDyFh4f7psPCwrRv375ybSzL0ocffqi4uDg5nU4988wz6tmzZ4V1paen+9673W65\n3e6GKhsAmiSPxyOPx1OndfgtMCzLqrbNnXfeqZycHIWEhGjbtm0aPXq0Dh8+XKHdjwMDAFDR9X9M\nL1y4sMbr8NspKafTqZycHN90Tk6OwsLCyrUJDQ1VSEiIJGnYsGEqLS3VuXPnGrVOAMAP/BYYvXv3\n1pEjR5Sdna2SkhJt2LBBycnJ5doUFBT4xjC8Xq+MMWrbtq0/ygWAm57fTkkFBQVp5cqVGjp0qMrK\nypSamqqoqCitWrVKkpSWlqa33npLL7zwgoKCghQSEqI333zTX+UCwE3PMk38MiTLsriSCgBqqDbf\nndzpDQCwhcAAANhCYAAAbCEwAAC2EBgAAFsIDACALQQGAMAWAgMAYAuBAQCwhcAAANhCYAAAbKn2\n4YMHDx7Unj17lJ2dLcuy5HK5lJiYqOjo6MaoDwBwg6jy4YPr169XRkaG2rVrp759+6pLly4yxig/\nP19er1eFhYV65JFHNHny5MauuRwePggANVeb784qjzDOnz+vnTt3KjQ0tNLlFy9e1Jo1a2q0MQBA\n08XjzQHgJlSvRxjXHD9+XBkZGcrOztbVq1d9G9qyZUvtqgQANEnVBsbo0aM1ffp0jRw5UgEBP1xU\nZVlWgxcGALixVHtKqm/fvvJ6vY1VT41xSgoAaq42353VBsb69et17NgxDR06VLfeeqtv/p133lm7\nKusZgQEANdcgYxgHDx7U+vXrtWvXLt8pKUnatWtXzSu8TlZWlubOnauysjJNnz5d8+fPr9Bmzpw5\n2rZtm0JCQrRmzRolJCTUebsAgJqrNjA2btyoEydO6JZbbqnXDZeVlWnWrFl699135XQ61adPHyUn\nJysqKsrXJjMzU0ePHtWRI0e0b98+zZw5U3v37q3XOgAA9lT7aJDY2FidP3++3jfs9XoVEREhl8ul\n4OBgTZgwQZs3by7XZsuWLUpJSZEk9evXTxcuXFBBQUG91wIAqF61Rxjnz59Xjx491KdPH98YRn1c\nVpuXl6fw8HDfdFhYmPbt21dtm9zcXDkcjnLtLCv9R1Pu//8CgIbXVIZQPR6PPB5PndZRbWAsXLiw\nwrz6uKzW7jquH5Sp7HPGpNe5HgBoztxut9xut2+6su/26lQZGMYYWZZVbgNVtakNp9OpnJwc33RO\nTo7CwsJ+sk1ubq6cTmettgcAqJsqxzDcbreefvppHT58uMKyr7/+WsuWLdOAAQNqveHevXvryJEj\nys7OVklJiTZs2KDk5ORybZKTk7Vu3TpJ0t69e9W6desKp6MAAI2jyiOM7du367XXXtPDDz+sL774\nQqGhoTLGqLi4WDExMZo0aZLefffd2m84KEgrV67U0KFDVVZWptTUVEVFRWnVqlWSpLS0NA0fPlyZ\nmZmKiIhQixYttHr16lpvDwBQN7YePlhWVqbCwkJJUvv27RUYGNjghdnFjXsAUHMNcqf3jY7AAICa\nq813Jz/RCgCwhcAAANhSbWCsWLGiQe70BgA0LdUGRkFBgfr06aNx48YpKyuL8QIAuEnZGvT+/vvv\ntX37dq1Zs0Yff/yxxo0bp9TUVHXr1q0xavxJDHoDQM012KB3QECAOnXqJIfDocDAQJ0/f1733nuv\n5s2bV6tCAQBNT7VHGMuXL9e6devUrl07TZ8+XWPGjFFwcLC+//57RUZG6tixY41Va6U4wgCAmmuQ\nH1A6d+6c/v3vf6tr167l5gcEBOidd96pWYUAgCaLG/cA4CbEjXsAgAZDYAAAbCEwAAC2EBgAAFsI\nDACALQQGAMAWAgMAYAuBAQCwhcAAANhS7aNBGsK5c+c0fvx4nTx5Ui6XS//617/UunXrCu1cLpd+\n9rOfKTAwUMHBwfJ6vX6oFgAg+ekIY+nSpUpKStLhw4c1aNAgLV26tNJ2lmXJ4/Fo//79hAUA+Jlf\nAmPLli1KSUmRJKWkpOjtt9+usi3PiQKAG4NfTkkVFBTI4XBIkhwOhwoKCiptZ1mWBg8erMDAQKWl\npWnGjBmVtktPT/e9d7vdcrvd9V0yADRpHo9HHo+nTutosKfVJiUl6cyZMxXmL168WCkpKeV+J7xt\n27Y6d+5chbb5+fnq3LmzvvnmGyUlJSkjI0OJiYnl2vC0WgCouQb5PYza2rFjR5XLHA6Hzpw5o06d\nOik/P18dO3astF3nzp0lSR06dNCYMWPk9XorBAYAoHH4ZQwjOTlZa9eulSStXbtWo0ePrtDmu+++\nU1FRkSTp0qVL2r59u2JjYxu1TgDA//HLDyidO3dO48aN06lTp8pdVnv69GnNmDFDW7du1fHjx3XP\nPfdIkq5evapJkybpz3/+c8UOcEoKAGqsNt+d/OIeANyE+MU9AECDITAAALYQGAAAWwgMAIAtBAYA\nwBYCAwBgC4EBALCFwAAA2EJgAABsITAAALYQGAAAWwgMAIAtBAYAwBYCAwBgC4EBALCFwAAA2EJg\nAABsITAAALYQGAAAW/wSGBs3blR0dLQCAwP1ySefVNkuKytLPXr0UGRkpJYtW9aIFQIArueXwIiN\njdWmTZt01113VdmmrKxMs2bNUlZWlg4dOqQ33nhDX375ZSNWCQD4sSB/bLRHjx7VtvF6vYqIiJDL\n5ZIkTZgwQZs3b1ZUVFQDVwcAqIxfAsOOvLw8hYeH+6bDwsK0b9++Stump6f73rvdbrnd7gauDgCa\nFo/HI4/HU6d1NFhgJCUl6cyZMxXmL1myRCNHjqz285Zl2d7WjwMDAFDR9X9ML1y4sMbraLDA2LFj\nR50+73Q6lZOT45vOyclRWFhYXcsCANSS3y+rNcZUOr937946cuSIsrOzVVJSog0bNig5ObmRqwMA\nXOOXwNi0aZPCw8O1d+9ejRgxQsOGDZMknT59WiNGjJAkBQUFaeXKlRo6dKh69uyp8ePHM+ANAH5k\nmar+xG8iLMuq8igFAFC52nx3+v2UFACgaSAwAAC2EBgAAFsIDACALQQGAMAWAgMAYAuBAQCwhcAA\nANhCYAAAbCEwAAC2EBgAAFsIDACALQQGAMAWAgMAYAuBAQCwhcAAANhCYAAAbCEwAAC2EBgAAFv8\nEhgbN25UdHS0AgMD9cknn1TZzuVyqVevXkpISFDfvn0bsUIAwPWC/LHR2NhYbdq0SWlpaT/ZzrIs\neTwetW3btpEqAwBUxS+B0aNHD9ttjTENWAkAwC6/BIZdlmVp8ODBCgwMVFpammbMmFFpu/T0dN97\nt9stt9vdOAUCQBPh8Xjk8XjqtA7LNNCf8ElJSTpz5kyF+UuWLNHIkSMlSQMHDtSzzz6rO++8s9J1\n5Ofnq3Pnzvrmm2+UlJSkjIwMJSYmlmtjWRZHIQBQQ7X57mywI4wdO3bUeR2dO3eWJHXo0EFjxoyR\n1+utEBgAgMbh98tqq0q47777TkVFRZKkS5cuafv27YqNjW3M0gAAP+KXwNi0aZPCw8O1d+9ejRgx\nQsOGDZMknT59WiNGjJAknTlzRomJiYqPj1e/fv109913a8iQIf4oFwCgBhzDaCyMYQBAzdXmu9Pv\np6QAAE0DgQEAsIXAAADYQmAAAGwhMAAAthAYAABbCAwAgC0EBgDAFgIDAGALgQEAsIXAAADYQmAA\nAGwhMAAAthAYAABbCAwAgC0EBgDAFgIDAGALgQEAsIXAuMF5PB5/l9BgmnPfJPrX1DX3/tWGXwJj\n3rx5ioqKUlxcnO655x59++23lbbLyspSjx49FBkZqWXLljVylTeG5vw/bXPum0T/mrrm3r/a8Etg\nDBkyRAcPHtSBAwfUvXt3/f3vf6/QpqysTLNmzVJWVpYOHTqkN954Q19++aUfqgUASH4KjKSkJAUE\n/LDpfv36KTc3t0Ibr9eriIgIuVwuBQcHa8KECdq8eXNjlwoAuMb42d13321ee+21CvM3btxopk+f\n7ptev369mTVrVoV2knjx4sWLVy1eNRWkBpKUlKQzZ85UmL9kyRKNHDlSkrR48WLdcsstuu+++yq0\nsyzL1nZ+yAwAQENrsMDYsWPHTy5fs2aNMjMztXPnzkqXO51O5eTk+KZzcnIUFhZWrzUCAOzzyxhG\nVlaWnn76aW3evFm33XZbpW169+6tI0eOKDs7WyUlJdqwYYOSk5MbuVIAwDV+CYzZs2eruLhYSUlJ\nSkhI0EMPPSRJOn36tEaMGCFJCgoK0sqVKzV06FD17NlT48ePV1RUlD/KBQBItRj1uIFs27bN/PKX\nvzQRERFm6dKl/i6n3nXt2tXExsaa+Ph406dPH3+XUydTp041HTt2NDExMb55Z8+eNYMHDzaRkZEm\nKSnJnD9/3o8V1k1l/XviiSeM0+k08fHxJj4+3mzbts2PFdbNqVOnjNvtNj179jTR0dFm+fLlxpjm\nsw+r6l9z2IeXL182ffv2NXFxcSYqKso8+uijxpja7bsmGxhXr1413bp1MydOnDAlJSUmLi7OHDp0\nyN9l1SuXy2XOnj3r7zLqxZ49e8wnn3xS7gt13rx5ZtmyZcYYY5YuXWrmz5/vr/LqrLL+paenm2ef\nfdaPVdWf/Px8s3//fmOMMUVFRaZ79+7m0KFDzWYfVtW/5rIPL126ZIwxprS01PTr18+8//77tdp3\nTfbRIDfLfRqmmVwFlpiYqDZt2pSbt2XLFqWkpEiSUlJS9Pbbb/ujtHpRWf+k5rP/OnXqpPj4eElS\ny5YtFRUVpby8vGazD6vqn9Q89mFISIgkqaSkRGVlZWrTpk2t9l2TDYy8vDyFh4f7psPCwnw7uLmw\nLEuDBw9W79699dJLL/m7nHpXUFAgh8MhSXI4HCooKPBzRfUvIyNDcXFxSk1N1YULF/xdTr3Izs7W\n/v371a9fv2a5D6/1r3///pKaxz78/vvvFR8fL4fDoYEDByo6OrpW+67JBobd+zSasg8++ED79+/X\ntm3b9Pzzz+v999/3d0kNxrKsZrdPZ86cqRMnTujTTz9V586d9ac//cnfJdVZcXGxxo4dq+XLlys0\nNLTcsuawD4uLi3Xvvfdq+fLlatmyZbPZhwEBAfr000+Vm5urPXv2aNeuXeWW2913TTYwbob7NDp3\n7ixJ6tChg8aMGSOv1+vniuqXw+Hw3dyZn5+vjh07+rmi+tWxY0ffP8Tp06c3+f1XWlqqsWPHasqU\nKRo9erSk5rUPr/Vv8uTJvv41t33YqlUrjRgxQv/73/9qte+abGA09/s0vvvuOxUVFUmSLl26pO3b\ntys2NtbPVdWv5ORkrV27VpK0du1a3z/S5iI/P9/3ftOmTU16/xljlJqaqp49e2ru3Lm++c1lH1bV\nv+awDwsLC32n0i5fvqwdO3YoISGhdvuuoUblG0NmZqbp3r276datm1myZIm/y6lXx48fN3FxcSYu\nLs5ER0c3+f5NmDDBdO7c2QQHB5uwsDDzyiuvmLNnz5pBgwY1+UsyjanYv5dfftlMmTLFxMbGml69\neplRo0aZM2fO+LvMWnv//feNZVkmLi6u3CWmzWUfVta/zMzMZrEPP/vsM5OQkGDi4uJMbGyseeqp\np4wxplb7zjKmGVwCAABocE32lBQAoHERGAAAWwgMAIAtBAYAwBYCA6hnV65c0YABA2r0SIkVK1Zo\n/fr1DVgVUHdcJQXUs1deeUVnz57VvHnzbH+mqKhIgwYNavI3hqF54wgDsOmjjz5SXFycrly5okuX\nLikmJkaHDh2q0O6NN97QqFGjJEkej0cDBgzQ6NGj1a1bNz366KNav369+vbtq169eun48eOSpNDQ\nULVr104HDx5s1D4BNdFgP9EKNDd9+vRRcnKyFixYoMuXL2vKlCnq2bNnuTZlZWX64osv1L17d9+8\nzz77TF999ZXatGmjn//855oxY4a8Xq9WrFihjIwMPffcc5Kkvn37as+ePYqOjm7UfgF2ERhADfz1\nr39V7969dfvttysjI6PC8sLCwgoP5evTp4/vqaAREREaOnSoJCkmJqbcQ+C6dOniO+IAbkSckgJq\noLCwUJcuXVJxcbEuX75caZvrhwVvvfVW3/uAgADfdEBAgK5evVruc039aa9o3ggMoAbS0tK0aNEi\n3XfffZo/f36F5e3bt1dxcXGt1p2fny+Xy1XHCoGGQ2AANq1bt0633nqrJkyYoEcffVQfffSRPB5P\nuTaBgYGKiYnR119/Lemnf2fg+mVer1eJiYkNVj9QV1xWC9SzNWvWqKCgoNIjkKpcvHhRgwYN0kcf\nfdSAlQF1Q2AA9aykpESDBw/W7t27bY9JrFixQm3bttXkyZMbuDqg9ggMAIAtjGEAAGwhMAAAthAY\nAABbCAwAgC0EBgDAFgIDAGDL/wOqiUdAB4d4TQAAAABJRU5ErkJggg==\n",
       "text": [
        "<matplotlib.figure.Figure at 0x62862d0>"
       ]
      }
     ],
     "prompt_number": 26
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "As expected, the robot drives in a straight line."
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "## Simulation - Need for control\n",
      "\n",
      "Currently our robot is driven in \"open-loop\" control. This means that an input signal is sent, but no output is measured. This is fine if our model is perfect, and there are no disturbances. But the real world isn't like that. For example, what if our motors aren't perfectly balanced, and had slightly different K values?\n",
      "\n",
      "To see, we'll change the K value of Motor 2 to 0.99. Just a small difference."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Change the value of K_2\n",
      "values = [5, 0.15, 2.0, 0.15, 50.0, 0.6, 0.05, 0.0001, 1.0, 0.99]\n",
      "\n",
      "#Re-run the straight line simulation\n",
      "ts = linspace(0, 10, 1000)\n",
      "us = 24*ones((len(ts),2))\n",
      "\n",
      "#Run the simulation\n",
      "x0 = [0,] * 15                         #Start out at origin, unmoving\n",
      "xs = odeint(right_hand_side, x0, ts, args=(ts, us, values))\n",
      "plt.plot(xs[:,0], xs[:,1])\n",
      "plt.title('Robot Position')\n",
      "plt.xlabel('x (m)')\n",
      "plt.ylabel('y (m)');"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "metadata": {},
       "output_type": "display_data",
       "png": "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\nl6hgiIiIS/4/jerXKIePMNAAAAAASUVORK5CYII=\n",
       "text": [
        "<matplotlib.figure.Figure at 0x5fbe0d0>"
       ]
      }
     ],
     "prompt_number": 27
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "After 10 seconds, the robot is already 2.5 m off it's intended course! Luckily, a simple feedback controller should be able to solve this.\n",
      "\n",
      "As our model is highly nonlinear, the first thing that should be done is to linearize it about our setpoint. However, currently the `LagrangesMethod` class doesn't have a linearization method. And because there are constraints, simply computing the Jacobian of the RHS isn't a valid solution. So an LQR controller, or other traditional state space design methods are out.\n",
      "\n",
      "To develop a controller for this robot, a simple linear model would need to be developed. This will be covered in part 2, but for now, let's just save the model to file so it can be opened later without rederiving:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#srepr returns the python code needed to recreate a\n",
      "#sympy expression\n",
      "from sympy.printing import srepr\n",
      "\n",
      "def save_sol(expr, file_path):\n",
      "    \"\"\"Save a sympy expression to file\n",
      "    \n",
      "    To reload the expression:\n",
      "    \n",
      "       import file_path\n",
      "       expression = file_path.load()\n",
      "    \n",
      "    \"\"\"\n",
      "    \n",
      "    fil = open(file_path, \"w\")\n",
      "    fil.write(\"from sympy import *\\n\")\n",
      "    fil.write(\"def load():\\n    expression=\")\n",
      "    fil.write(srepr(expr))\n",
      "    fil.write(\"\\n    return expression\")\n",
      "    fil.close()"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 28
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#Save the rhs for later evaluation\n",
      "save_sol(rhs, \"robot_rhs.py\")"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 29
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "This can later be reloaded by calling:\n",
      "\n",
      "    import robot_rhs\n",
      "    rhs = robot_rhs.load()\n",
      "    \n",
      "In a later example, a controller will be developed using this model for simulation, so it's nice not to have to rederive the expression.\n",
      "\n",
      "Overall, the `sympy.physics.mechanics` workflow for modeling multibody dynamics is easy to use. As shown above, the tools for generating a model based off of its components (wheels, body, etc...), solving for the system dynamics, and then simulating the resulting expression are provided in an intuitive manner. While larger/more complicated models may take some time (I've gotten the [n-pendulum example](https://github.com/pydy/pydy_examples/tree/master/npendulum) to solve for up to 25 links), the resulting expression in symbolic form allows for simulation and analysis in a way that traditional numerical methods cannot provide. With some improvements to the simplification routines, and the addition of a linearization routine the hope is that the complicated expressions shown here can be simplified to \"human readable form\". This would allow for fast run-time due to simplified expressions, as well as easier analysis.\n",
      "\n",
      "\n",
      "*Note: The code used here was developed as part of the [pydy](https://pydy.org/) project. Other awesome portions of the `pydy` workflow have not been demonstrated here. Some work is currently being done on [code generation](https://github.com/pydy/pydy-code-gen) to generate code in `C`, `FORTRAN`, or other language from a `sympy` expression. This would allow for faster simulations, and easy integration into embedded controllers or estimators. There is also some work being done on [visualization](https://github.com/pydy/pydy-viz), resulting in an animation of the system dynamics running in a browser.*\n",
      "\n",
      "*This IPython notebook may be downloaded from [here](https://github.com/jcrist/pydy_examples/tree/master/differential_drive)*"
     ]
    }
   ],
   "metadata": {}
  }
 ]
}
